#include <arpa/inet.h>
#include <sys/types.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include <signal.h>
#include <time.h>

int tcp_fd;
pthread_mutex_t lock;
typedef struct client_info
{
    struct sockaddr_in client;
    int con_fd;

    struct client_info *next;
    struct client_info *prev;
} client_info;

struct client_info *head;

void sig_exit(int);
void init_client_list();
void add_client_list(struct client_info *);
void del_client_list(int);

void str_err(char *);
int get_tcp();
void *func(void *);

int main(int argc, char *argv[])
{
    signal(SIGINT,sig_exit);
    int tcp_fd = get_tcp();

    int con_fd;
    printf("wait process\n");
    listen(tcp_fd, 2);

    struct sockaddr_in client_addr;
    socklen_t len = sizeof(client_addr);
    pthread_t tid;
    struct client_info client_tmp;
    while (1)
    {
        con_fd = accept(tcp_fd, (struct sockaddr *)&client_addr, &len);
        if (con_fd < 0)
            str_err("connect fail");
        client_tmp.con_fd = con_fd;
        client_tmp.client = client_addr;
        pthread_create(&tid, NULL, func, (void *)&client_tmp);
    }

    close(tcp_fd);
    return 0;
}

void str_err(char *data)
{
    fprintf(stderr, "%s errno\n", data);
    perror("");
    exit(-1);
}

int get_tcp()
{
    init_client_list();
    int tcp_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (tcp_fd < 0)
        str_err("tcp_fd socket fail");

    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));

    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(50000);

    inet_pton(AF_INET, "0.0.0.0", &server_addr.sin_addr);

    int on = 1; // 写一，表示启用
    setsockopt(tcp_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));

    int ret = bind(tcp_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
    if (ret != 0)
        str_err("bind fail");

    return tcp_fd;
}

void init_client_list()
{
    head = malloc(sizeof(struct client_info));
    head->next = head->prev = head;
}

void add_client_list(struct client_info *data)
{
    struct client_info *new;
    new = malloc(sizeof(struct client_info));

    new->next = head;
    new->prev = head->prev;

    head->prev->next = new;
    head->prev = new;

    new->client = data->client;
    new->con_fd = data->con_fd;

    printf("add:ip:%s port:%hu\n", inet_ntoa(new->client.sin_addr), ntohs(new->client.sin_port));
}

void del_client_list(int con_fd)
{
    struct client_info *tmp = head->next;

    while (tmp != head)
    {
        if (tmp->con_fd == con_fd)
        {
            printf("disconnect:ip:%s port:%hu\n", inet_ntoa(tmp->client.sin_addr),
                   ntohs(tmp->client.sin_port));
            tmp->prev->next = tmp->next;
            tmp->next->prev = tmp->prev;
            free(tmp);
            tmp = NULL;
            return;
        }
        tmp = tmp->next;
    }
}

void *func(void *argv)
{
    struct client_info *client = (struct client_info *)argv;
    int con_fd = client->con_fd;

    pthread_mutex_lock(&lock);
    add_client_list(client);
    pthread_mutex_unlock(&lock);

    int file_fd;
    char path[1024];
    sprintf(path,"data/%s_%hu.txt",inet_ntoa(client->client.sin_addr),
               ntohs(client->client.sin_port));
    file_fd = open(path,O_RDWR | O_CREAT | O_TRUNC,0644);
    if(file_fd < 0)
      str_err(path);
    time_t current_time;
    struct tm *local_time;

    time(&current_time);  
    local_time = localtime(&current_time);  
    dprintf(file_fd,"file is designed in %s",asctime(local_time));

    char buf[1024];
    int ret;
    while (1)
    {
        memset(buf, 0, 1024);
        ret = read(con_fd, buf, 1024);
        if (ret == 0 || !strcmp(buf, "bye"))
        {
            pthread_mutex_lock(&lock);
            del_client_list(con_fd);
            pthread_mutex_unlock(&lock);
            break;
        }

        dprintf(file_fd,"%s\n",buf);
        printf("rec ip is %s,prot is %hu recv is %s\n", inet_ntoa(client->client.sin_addr),
               ntohs(client->client.sin_port), buf);
    }

    close(file_fd);
    close(con_fd);
    pthread_exit(NULL);
}

void sig_exit(int num)
{
    struct client_info* p;
    p = head->next;
    int con_fd;
    while(p != head)
    {
        con_fd = p->con_fd;
        send(con_fd,"client close",strlen("client close"),0);
        close(con_fd);
        p = p->next;
    }

    close(tcp_fd);
    exit(-1);
}
